


#require the tk graphics library
require 'tk'
require 'tkextlib/tile' #required for more advance tK widgets

#A Class Used to generate a GUI for controlling a P2AT robot in the USARSIM world.
#The GUI is made by using the TK library
class GUI_Interface

  #creates the basic interface of the GUI, creates buttons and labels, this interface
  #is designed for the USARSIM's P2AT Bot
 def draw_interface
 #draw interface
 
    #draw the main window 
    @root=TkRoot.new{title 'GUI USARSIM CONTROLLER' }
    
    #draw empty column on the left and right side
    TkLabel.new(@root) {text "       "}.grid "column"=>0,"row"=>0
    TkLabel.new(@root) {text "       "}.grid "column"=>4,"row"=>0
    
    #draw title
    TkLabel.new(@root) {text "P2AT USARSIM ROBOT CONTROLLER"}.grid "column"=>2,"row"=>0
   
    #add line before frame
    TkLabel.new(@root) {text ""}.grid "column"=>1,"row"=>1
   
   #draw robot status frame
    @notebook = Tk::Tile::Notebook.new(@root).grid "column"=>1,"row"=>2,"columnspan"=>3,"sticky" => "we"
    @robstat = TkFrame.new(@notebook);
    @notebook.add @robstat,"text"=>"Robot status"
    
    #draw status
    TkLabel.new(@robstat) {text " "}.grid "column"=>0,"row"=>0
    @TimeLabel=TkLabel.new(@robstat) {text "Field Time: "}.grid "column"=>0,"row"=>1
    @BattLabel=TkLabel.new(@robstat) {text "Battery Life :"}.grid "column"=>0,"row"=>2
    @GlobLabelX=TkLabel.new(@robstat) {text "INS XPOS="}.grid "column"=>0,"row"=>3
    @GlobLabelY=TkLabel.new(@robstat) {text "INS YPOS="}.grid "column"=>0,"row"=>4
    @GlobLabelZ=TkLabel.new(@robstat) {text "INS ZPOS="}.grid "column"=>0,"row"=>5
    @GlobLabelROW=TkLabel.new(@robstat) {text "INS ROW="}.grid "column"=>0,"row"=>6
    @GlobLabelPICTH=TkLabel.new(@robstat) {text "INS PITCH="}.grid "column"=>0,"row"=>7
    @GlobLabelYAW=TkLabel.new(@robstat) {text "INS YAW="}.grid "column"=>0,"row"=>8
    TkLabel.new(@robstat) {text "        "}.grid "column"=>1,"row"=>9
     
    
    #draw sensors
    @SonarF=[]
    8.times{|i|
    @SonarF[i+1]=TkLabel.new(@robstat) {text "SonarF#{i+1} :"}.grid "column"=>2,"row"=>i+1
    }
    @SonarR=[]
    TkLabel.new(@robstat) {text "       "}.grid "column"=>3,"row"=>0
    TkLabel.new(@robstat) {text "       "}.grid "column"=>4,"row"=>0
    8.times{|i|
  @SonarR[i+1]=TkLabel.new(@robstat) {text "SonarR#{i+1} :"}.grid "column"=>5,"row"=>i+1
    }
    
  
   #draw control frame
   @conframe=TkFrame.new(@root).grid "column"=>1 ,"row"=>3,"columnspan"=>3,"sticky" => "we","pady"=>10
   @conframe.configure("borderwidth" => 1,"relief"=>"ridge")
  
 #draw Controllers
   TkLabel.new(@conframe) {text "CONTROLLER"}.grid "column"=>0,"row"=>2  
   @upbut=TkButton.new(@conframe){text"Forward"}.grid "column"=>0,"row"=>3
   @leftbut=TkButton.new(@conframe){text"  Left "}.grid "column"=>0,"row"=>4,"sticky" => "w"
   @rightbut=TkButton.new(@conframe){text" Right  "}.grid "column"=>0,"row"=>4,"sticky" => "e"
   @downbut=TkButton.new(@conframe){text"Backward"}.grid "column"=>0,"row"=>5
   @upbut.command { 
     Thread.new{
     @robot.smartfor(@speed_dial.get.to_f,2.0)  if @brain_opt.get=="No_Brain"
     }
   }
   @leftbut.command {
     Thread.new{
     @robot.smartleft(@turn_dial.get.to_f ) if @brain_opt.get=="No_Brain"
     }
   }
   @rightbut.command {
     Thread.new{
     @robot.smartright(@turn_dial.get.to_f ) if @brain_opt.get=="No_Brain"
     }
    }
   @downbut.command {
     Thread.new{
     @robot.smartback(@speed_dial.get.to_f,2.0) if @brain_opt.get=="No_Brain"
     }
   }
   TkLabel.new(@conframe) {text "        "}.grid "column"=>1,"row"=>0 #add empty column
  
   #create a combo box named brain_opt
   @brain_opt = Tk::Tile::Combobox.new(@conframe) { textvariable $countryvar}.grid "column"=>2,"row"=>3
   @brain_opt.configure("values"=>"No_Brain Rand_Brain Maze_Brain","width"=> 10) 
   
   #event on combo box change
   @brain_opt.bind("<ComboboxSelected>") { puts "Brain mode set to " <<@brain_opt.get}
   @brain_opt.set("No_Brain") #Set default to no brain
   @brain_opt.state("readonly") # Set the combo box for read only mode
   @brain_mode_lab=TkLabel.new(@conframe) {text "Brain Mode " }.grid "column"=>2,"row"=>2
   
   #create a scale box to set the speed
   @speed_dial=TkSpinbox.new(@conframe) {from 0.0; to 2.0; increment 0.1; width 5;}.grid "column"=>2,"row"=>4,"sticky"=>"e"
   @speed_dial.set(1.0)
   TkLabel.new(@conframe) {text "Distance: "}.grid "column"=>2,"row"=>4,"sticky"=>"w"
   
   #create a scale box to set the turn 
   @turn_dial=TkSpinbox.new(@conframe) {from 0.0; to 3.0; increment 0.2; width 5;}.grid "column"=>2,"row"=>5,"sticky"=>"e"
   @turn_dial.set(1.5)
   TkLabel.new(@conframe) {text "Turn: "}.grid "column"=>2,"row"=>5,"sticky"=>"w"
   
   TkLabel.new(@conframe) {text "        "}.grid "column"=>3,"row"=>0 #add empty column 
   
   #draw Camera Controls   
    TkLabel.new(@conframe) {text "Camera Controls"}.grid "column"=>4,"row"=>2 
    @mcam=TkButton.new(@conframe){text "Mount_Cam"}.grid "column"=>4,"row"=>3
    @ucam=TkButton.new(@conframe){text "Unmount Cam"}.grid "column"=>4,"row"=>4
    @mcam.command{@robot.mountcam("10.1.157.59")}
    @ucam.command{@robot.unmountcam("10.1.157.59")}
   
   TkLabel.new(@conframe) {text ""}.grid "column"=>0,"row"=>6 #add final line before ending the control frame

    #draw canvas
    TkLabel.new(@root) {text "          "}.grid "column"=>2,"row"=>4
    TkLabel.new(@root) {text "Camera"}.grid "column"=>2, "row"=>5
    @canvas=TkCanvas.new(@root).grid "column"=>1, "row"=>6 , "columnspan" => 3
  
   @gui_img=TkPhotoImage.new('file'=>'images/loading.gif')
   @canvas_t=TkcImage.new(@canvas,200,100,'image'=>@gui_img)    
    
 end
  
  #this function is executed when a new GUI_Interface object is created
  def initialize 
    #sets the default variables
    #@control="No_Brain" #control flag(Used with buttons)
    @server_ip="127.0.0.1" #default connecting ip
    @server_port=3000 #default server port
    @robot_type="USARBot.P2AT" #default robot type
    @robot_name=""#default robot name
    @gui_opengl=true #enable opengl
    @robot_start=""#default null
    
    self.loadconfig #reads the values from the config file
    self.draw_interface # draws the interfaces
      
  end  
  
  #executes the GUI, including a timer which connects to the USARSIM server
  #periodically every millisecond to updates the sensor and status infomation and also
  #check for user commands 
  def run
    
    
    #connect to robot
    @robot=Robot.new(@server_ip,@server_port)
    @robot.init(@robot_type,@robot_start,@robot_name)  
   
    #create Log instance
    @log=RobotLog.new(@robot,@gui_opengl)
    
    #create Brain instance
    @brain=Idm.new(@robot)
    
    #create map instance
    @map=Robotmap.new(@robot)
    @robot.recieve
    sleep(2)
    @map.autoupdatemap(true)
    @map.autologmap(true)

    #run collision guard 
    @robot.colguardmode
    
    #reset Logs
    @log.reset_logs
    
    #orders the program to update the robot camera 
    @cam_ip='127.0.0.1'
    @cam_port=5003
    @cam_imgserver=GetImage.new(@cam_ip,@cam_port,1,1)
    puts @cam_imgserver.status
    if(@cam_imgserver.status==1)
     
     #@cam_imgserver.batchjpg 
     @cam_imgserver.batchjpg_proc
     
     @cam_en=1
     #resets the Unreal Client
     @robot.unmountcam("10.1.157.59")
     @robot.mountcam("10.1.157.59")
     puts "connection established"
    else
     @gui_img=TkPhotoImage.new('file'=>'images/error.gif')
     @canvas_t=TkcImage.new(@canvas,200,100,'image'=>@gui_img)  
     @cam_en=0
     #resets the Unreal Client
     @robot.unmountcam("10.1.157.59")
     @robot.mountcam("10.1.157.59")
     puts "connection error"
    end
    @counter=0
    
   
      
    #update picture begin new thread
    #Thread.new{
    #while true
    #    sleep(0.2)     
    #end    
    #}
    
    #inside the timer
    @maintimer=TkAfter.new(1,-1,proc{
        

    #tick Process   
    Thread.new{
    @robot.recieve
    }
    
    @TimeLabel.configure('text'=>"Field Time: #{@robot.status[0]}")
    @BattLabel.configure('text'=>"Battery Life: #{@robot.status[1]}")

    #draw brain mode label
    @brain_mode_lab.configure("text"=>"Brain Mode : #{@brain_opt.get}")
        
    #draw sensors

    #find INS sensor
    @GlobLabelX.configure("text"=>"INS XPOS=#{@robot.ins[0]} ") unless @robot.ins[0]==nil
    @GlobLabelY.configure("text"=>"INS YPOS=#{@robot.ins[1]} ") unless @robot.ins[1]==nil
    @GlobLabelZ.configure("text"=>"INS ZPOS=#{@robot.ins[2]} ") unless @robot.ins[2]==nil
    @GlobLabelROW.configure("text"=>"INS ROW=#{@robot.ins[3]} ") unless @robot.ins[3]==nil
    @GlobLabelPICTH.configure("text"=>"INS PITCH=#{@robot.ins[4]} ") unless @robot.ins[4]==nil
    @GlobLabelYAW.configure("text"=>"INS YAW=#{@robot.ins[5]} ") unless @robot.ins[5]==nil
        
    #sonarsensors
    i=0
    j=0  
     while i<8
       @SonarF[i+1].configure("text"=>"SonarF#{i+1} :#{@robot.fSONARSEN[i]}") unless @robot.fSONARSEN[i]==nil
       i=i+1
     end
    while j<8
       @SonarR[j+1].configure("text"=>"SonarR#{j+1} :#{@robot.rSONARSEN[j]}") unless @robot.rSONARSEN[j]==nil
       j=j+1
     end   
    
     
     #orders the robot to move around randomly
    # @brain.mazemove2 if @brain_opt.get=="Rand_Brain"
    # @brain.smartmove if @brain_opt.get=="Maze_Brain"

     #Log Robot position
     @log.position_log
    
    if @counter>5 and @cam_en==1
    @counter=0
    @gui_img=TkPhotoImage.new('file'=>'Robot1Camera1.gif') 
    @canvas_t=TkcImage.new(@canvas,200,100,'image'=>@gui_img)
 end
    
    @counter+=1

    
    })
    @maintimer.start
    Tk.mainloop
  end
  
  #This function loads the settings from the config file 
  def loadconfig()
    #try to read the config file (usarsimGUI.cfg)
   @CONFIGFILENAME="usarsimGUI.cfg"
   raise "Cannot read config file #{@CONFIGFILENAME}"  unless (File.file?(@CONFIGFILENAME))
   open(@CONFIGFILENAME,"r"){|file|
   raise "config file corrupted" unless (file.readline()=~ /\[USARSIM_GUI_CONTROLLER\]/)
   
   #load the config file data
    file.each_line() { |line|      
       #set the server_IP from the config file
       if line =~ /\[SERVER_IP=([A-Za-z.0-9]*)\]/
         ipadd=$1
        if ipadd =~ /^\b((25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)\.){3}(25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)\b$/  #check if the type 
         @server_ip=ipadd 
          puts "setting the connecting ip to  #{@server_ip}"
        else
         puts "incorrect ip address parameter, defaulting to #{@server_ip}"
       end
      end
      #set the port no from the config file
         if line =~ /\[PORT_NO=([A-Za-z.0-9]+)\]/
         portno=$1
         unless portno=~ /\D/ #unless it has no digits
          @server_port=portno.to_i
          puts "setting the port number to #{@server_port} "
        else
         puts "cannot set the port no, defaulting to #{@server_port}"
       end
      end
     #set the robot type 
         if line =~ /\[ROBOT_TYPE=([A-Za-z.0-9]+)\]/
         rotype=$1
         if rotype=~ /USARBot.P2AT|^P2AT/i #if the robot type is P2AT
          @robot_type="USARBot.P2AT"
          puts "setting the robot type to #{@robot_type} "
        else
         puts "robot type not found , defaulting to #{@robot_type}"
       end
      end
   #set the robot name
    if line =~ /\[ROBOT_NAME=([A-Za-z.0-9]*)\]/
         roname=$1
         if roname=~/([A-Za-z.0-9]+)/i #if the robot name isn't null
          puts "setting the robot name to #{roname} "
          @robot_name=roname
         else
         puts "robot type not found, leaving the naming to the server"
         @robot_name=""
       end
      end
   #set the robot starting position
       if line =~ /\[ROBOT_START=([A-Za-z.\- ,0-9]*)\]/i
         rostart=$1
         if rostart=~/([A-Za-z.\- ,0-9]*)/i #if the robot starting position isn't null
          puts "setting the starting position to #{rostart} "
          @robot_start=rostart
         else
         puts "robot type not found, leaving the naming to the server"
         @robot_start=""
       end
      end
      
    }
   }  
  end
  
end 

#Note to self: Made recieve a thread

